using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using SpringProxAPI;
using CodePPC.General;

namespace NFC_decoder
{
  public partial class NFCDecoderFrm : Form
  {
    public NFCDecoderFrm()
    {
      InitializeComponent();
      set_URI_Codes();
      // Variable initialization

      workerAllowed = true;
      workerRunning = false;
      workerNextTask = 0;
      workerDoneTask = 0;
      workerResult = false;
      readerActive = false;

      workerEvent = new ManualResetEvent(false);
      workerThread = new Thread(new ThreadStart(workerLoop));

      // Start Thread
      workerThread.Start();
      workerStart(WORK_OPEN_READER);
    }

#region Mifare reading 
    public byte[] MIFARE_KEY_NFC =
      new byte[6] { 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7 };
    public byte[] MIFARE_KEY_MAD =
      new byte[6] { 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5 };
    public byte[] card_data = new byte[48];
    
    private const byte AID_1 = 0x03;
    private const byte AID_2 = 0xE1;

    private bool NFC_Content;

    private bool ReadCardMifare()
    {
      short rc;
      byte l = 48;
      byte[] t = new byte[l];

      NFC_Content = false;
      /* step 1 : read MAD sector */
      rc = SPROX.MifStReadSector(null, 0, t, MIFARE_KEY_MAD);
      if (rc != SPROX.MI_OK)
        return false;

      /* step 2 : determine the number of sectors to read */
      int nb_sectors = 0;

      for (int i = 18; i < t.Length; i = i + 2)
      {
        if ((t[i] == AID_1) && (t[i + 1] == AID_2))
          nb_sectors++;
      }

      if (nb_sectors > 0)
        NFC_Content = true;

      card_data = new byte[nb_sectors * l];
      int offset_card = 0;
      /* for each sector, get data  */
      for (int sector = 0; sector < nb_sectors; sector++)
      {
        rc = SPROX.MifStReadSector(null,(byte) (sector+1), t, MIFARE_KEY_NFC);
        if (rc != SPROX.MI_OK)
          return false;
        for (int cpt = 0; cpt < l; cpt++)
          card_data[offset_card + cpt] = t[cpt];
        offset_card += l;
      }

      return true;
    }

    public bool ReadCardMifareUL()
    {
      short rc;
      byte l = 16;
      byte[] t = new byte[l];

      int offset_card = 0;

      NFC_Content = false;
      Card_URI_Tot = "";
      /* read sector 3 to find NFC header */
      rc = SPROX.MifRead(null, 3, t);
      if (rc != SPROX.MI_OK)
        return false;
      if ((t[0] == 0xE1) && (t[1] == 0x10) && (t[2] == 0x06))
      {

        NFC_Content = true;

        for (int i = 1; i < 4; i++)
        {
          rc = SPROX.MifRead(null, (byte)(4 * i), t);
          if (rc != SPROX.MI_OK)
            return false;
          for (int cpt = 0; cpt < l; cpt++)
            card_data[offset_card + cpt] = t[cpt];
          offset_card += l;

        }
      }
      return true;
    }
#endregion

#region Worker (background thread)

    private Thread workerThread;
    private ManualResetEvent workerEvent;
    private bool workerAllowed;
    private bool workerRunning;
    private int workerNextTask;
    private int workerDoneTask;
    private bool workerResult;
    private bool readerActive;

    private const int WORK_NONE = 0;
    private const int WORK_OPEN_READER = 1;
    private const int WORK_FIND_CARD = 2;
    private const int WORK_READ_CARD = 3;

    private int TimerCpt = 0;

    private void tmrBasetime_Tick(object sender, System.EventArgs e)
    {
      if (workerNextTask != 0)
        return;
      if (workerDoneTask != 0)
        return;
      if (SameCardPresent())
        return;

      /* Next run in 200ms */
      timerBase.Interval = 200;

      /* Polling */
      workerStart(WORK_FIND_CARD);
    }


    private bool OpenReaderTask()
    {
      short rc;

      if (readerActive)
        return true;

      /* In case of a communcation error, we must close it before */
      SPROX.ReaderClose();

      /* Try to open the reader */
      rc = SPROX.ReaderOpen("");
      if (rc == SPROX.MI_OK)
      {
        readerActive = true;
        SPROX.ControlLed(0, 0, 0);
      }

      return readerActive;
    }

    private bool ReadCardTask()
    {
      bool rc = false;
      switch (card_is_type)
      {
        case CARD_MIFARE_1K:
        case CARD_MIFARE_4K:
          rc = ReadCardMifare();
          decodeTLV(card_data);
          type_NDEF = 0;
          decodeNDEF(V_VALUE);
          break;
        case CARD_MIFARE_UL:
          rc = ReadCardMifareUL();
          decodeTLV(card_data);
          type_NDEF = 0;
          decodeNDEF(V_VALUE);
          break;

      }

      /* Power down the antenna to preserve batteries */
      if (SPROX.ControlRF(0) != SPROX.MI_OK)
      {
        /* Fatal error, this one shoul'd never fail ! */
        readerActive = false;
        SPROX.ReaderClose();
        return false;
      }

      return rc;
    }


    private void workerLoop()
    {
      int task;
      bool rc;

      try
      {
        workerRunning = true;
        while (workerAllowed)
        {
          workerEvent.WaitOne();
          if (!workerAllowed)
            break;
          task = workerNextTask;

          /* Until reader is found, the only allowed task is to open it... */
          if (!readerActive)
            task = WORK_OPEN_READER;

          

          switch (task)
          {
            case WORK_OPEN_READER:
              rc = OpenReaderTask();
              break;
            case WORK_FIND_CARD:
              rc = FindCardTask();
              break;
            case WORK_READ_CARD:
              rc = ReadCardTask();
              break;
            default:
              rc = false;
              break;
          }

          workerResult = rc;
          workerDoneTask = task;
          workerNextTask = 0;
          workerEvent.Reset();

          Invoke(new EventHandler(workerCallback));
        }
      }
      catch (Exception e) { }
      workerRunning = false;
    }

    private void workerDone(int task, bool rc)
    {
      //labelCardResult.Visible = false;
      labelInfo.ForeColor = Color.Black;
      switch (task)
      {
        case WORK_OPEN_READER:
          labelInfo.Text = "Opening reader...";
          break;



        case WORK_FIND_CARD:
          labelInfo.Text = "Insert card ";
          if (rc)
          {
            TimerCpt = 0;
            if ((card_is_type != CARD_MIFARE_1K) && (card_is_type != CARD_MIFARE_4K) && (card_is_type != CARD_MIFARE_UL))
            {
              labelInfo.ForeColor = Color.Red;
              labelInfo.Text = "Unsupported card type";
            }
            else
            {
              workerStart(WORK_READ_CARD);
            }
          }
          else
          {
            
            /* lost connection with reader */
            if ((readerAnswer == -247) || (readerAnswer == -245))
            {
              labelInfo.Text = "Opening reader...";
            }
            else
            {
              /* increase timer to save power consumption */
              if (TimerCpt < 3)
                TimerCpt++;
               timerBase.Interval += 100 * TimerCpt;
            }
            
          }

          break;
        case WORK_READ_CARD:
          panelPrompt.Visible = false;
          labelInfo.Text += rc.ToString()+"- "+NFC_Content.ToString();
          if (rc)
          {
            /* Next run in 1.5 seconds */
            timerBase.Interval = 1500;
            if ((NFC_Content) && (Card_URI_Tot.Length>0))
            {
              labelInfo.Text = "URL found " + Card_URI_Tot;
              OpenPanel(Card_URI_Tot);
            }
            else
            {
              labelInfo.ForeColor = Color.Red;
              if (!NFC_Content)
                labelInfo.Text = "No NFC Content on this card ";
              else
                labelInfo.Text = "No Valid NDEF found on this card";
            }
         }else
         {
           /* Next run in 1 seconds */
           timerBase.Interval = 1000;
           labelInfo.ForeColor = Color.Red;
           labelInfo.Text = "Cannot read card ";
         }


          break;


        default:
          break;
      }
    }

    private void workerStart(int task)
    {
      workerDoneTask = 0;
      workerNextTask = task;
      workerEvent.Set();
    }


    private void workerCallback(object sender, EventArgs e)
    {
      if (workerDoneTask != 0)
      {
        workerDone(workerDoneTask, workerResult);
        workerDoneTask = 0;
      }
    }

#endregion

#region ISO/IEC 14443 card detection and selection

    private const int CARD_UNKNOWN = -1;
    private const int CARD_NONE = 0;
    private const int CARD_MIFARE_1K = 1;
    private const int CARD_MIFARE_4K = 2;
    private const int CARD_MIFARE_UL = 3;
    private const int CARD_DESFIRE = 4;
    private short readerAnswer = 0;

    public bool card_is_tcl;
    public bool card_is_type_a;
    public bool card_is_type_b;
    public int card_is_type;

    public byte[] card_iso_a_atq = new byte[2];
    public byte[] card_iso_a_sak = new byte[1];
    public byte[] card_iso_a_snr = new byte[12];
    public byte card_iso_a_snrlen;
    public byte[] card_iso_b_atq = new byte[11];

    public byte[] card_tcl_ats = new byte[32];
    public byte card_tcl_atslen;

    private bool SameCardPresent()
    {
      short rc;

      byte[] new_card_iso_a_atq = new byte[2];
      byte[] new_card_iso_a_sak = new byte[1];
      byte[] new_card_iso_a_snr = new byte[12];
      byte new_card_iso_a_snrlen;
     

      new_card_iso_a_snrlen = 12;
      rc =
        SPROX.A_SelectAny(new_card_iso_a_atq, new_card_iso_a_snr,
                          ref new_card_iso_a_snrlen, new_card_iso_a_sak);
      

      if (rc != SPROX.MI_OK)
        return false;

      
      if (new_card_iso_a_snrlen != card_iso_a_snrlen)
        return false;



      for (int i = 0; i < new_card_iso_a_snrlen; i++)
      {
        if (new_card_iso_a_snr[i] != card_iso_a_snr[i])
          return false;
      }

      return true;

    }

    private bool FindCardTask()
    {
      short rc;

      /* We must halt the card first (in case we were already connected) */
      if (card_is_tcl)
      {
        SPROX.Tcl_Deselect(0xFF);
      }
      else if (card_is_type_a)
      {
        SPROX.A_Halt();
      }
      else if (card_is_type_b)
      {
        SPROX.B_Halt(card_iso_b_atq);
      }

      card_is_type_a = false;
      card_is_type_b = false;
      card_is_tcl = false;

      if (SPROX.ControlRF(1) != SPROX.MI_OK)
      {
        /* Fatal error, this one shoul'd never fail ! */
        readerActive = false;
        SPROX.ReaderClose();
        return false;
      }

      /* At first try to find an ISO-14443-A card */
      SPROX.SetConfig(SPROX.CFG_MODE_ISO_14443_A);

      card_iso_a_snrlen = 12;
      rc =
        SPROX.A_SelectAny(card_iso_a_atq, card_iso_a_snr,
                          ref card_iso_a_snrlen, card_iso_a_sak);

      if (rc == SPROX.MI_OK)
      {
        /* A card has been found */
        card_is_type = CARD_UNKNOWN;

        if ((card_iso_a_sak[0] & 0x20) != 0)
        {
          /* Card support T=CL, try to activate it */
          card_tcl_atslen = 32;
          rc = SPROX.TclA_GetAts(0xFF, card_tcl_ats, ref card_tcl_atslen);
          if (rc == SPROX.MI_OK)
          {
            /* Found a type A T=CL card */
            card_is_type_a = true;
            card_is_tcl = true;

            if ((card_iso_a_atq[1] == 0x03) && (card_iso_a_atq[0] == 0x44))
            {
              card_is_type = CARD_DESFIRE;
            }
            return true;
          }
        }
        else
        {
          /* No T=CL support, but type A card found anyway */
          card_is_type_a = true;

          if ((card_iso_a_atq[1] == 0x00) && (card_iso_a_atq[0] == 0x04))
          {
            card_is_type = CARD_MIFARE_1K;
          }
          else
            if ((card_iso_a_atq[1] == 0x00) && (card_iso_a_atq[0] == 0x02))
            {
              card_is_type = CARD_MIFARE_4K;
            }
            else
              if ((card_iso_a_atq[1] == 0x00) && (card_iso_a_atq[0] == 0x44))
              {
                card_is_type = CARD_MIFARE_UL;
              }

          return true;
        }

        /* Type A card found but with error */
        goto failed;
      }

      /* Nox try to find an ISO-14443-B card */
      SPROX.SetConfig(SPROX.CFG_MODE_ISO_14443_B);

      rc = SPROX.B_SelectAny(0x00, card_iso_b_atq);

      if (rc == SPROX.MI_OK)
      {
        /* A card has been found */
        card_is_type = CARD_UNKNOWN;

        if ((card_iso_b_atq[10] & 0x01) != 0)
        {
          /* Card support T=CL, try to activate it */
          card_tcl_atslen = 32;
          rc =
            SPROX.TclB_Attrib(card_iso_b_atq, 0xFF, card_tcl_ats,
                              ref card_tcl_atslen);
          if (rc == SPROX.MI_OK)
          {
            /* Found a type B T=CL card */
            card_is_type_b = true;
            card_is_tcl = true;
            return true;
          }
        }
        else
        {
          /* No T=CL support, but type Bcard found anyway */
          card_is_type_b = true;
          return true;
        }

        /* Type B card found but with error */
        goto failed;
      }

    failed:
      /* Power down the antenna to preserve batteries */
      if (SPROX.ControlRF(0) != SPROX.MI_OK)
      {
        /* Fatal error, this one shoul'd never fail ! */
        readerActive = false;
        SPROX.ReaderClose();
      }
      return false;
    }

#endregion

#region TLV decoding

    // TLV Constants

    const int INDEX_T = 0;
    const int INDEX_L = 1;
    const byte NULL_TLV = 0x00;
    const byte LOCK_CONTROL_TLV = 0x01;
    const byte MEMORY_CONTROL_TLV = 0x02;
    const byte NDEF_MESSAGE_TLV = 0x03;
    const byte PROPRIETARY_TLV = 0xFD;
    const byte TERMINATOR_TLV = 0xFE;

    const byte FLAG_L_3_BYTES = 0xFF;

    const int MAX_L = 65534;//FFFE

    string T_STR = "";
    int L_VALUE = 0;
    byte[] V_VALUE;


    private void decodeTLV(byte[] dataIn)
    {
      V_VALUE = new byte[0];
      if (dataIn.Length > 2)
      {
        // T
        byte t = dataIn[INDEX_T];
        T_STR = decodeT(t);

        // L
        int start_v = INDEX_L + 1;
        if (dataIn[INDEX_L] == FLAG_L_3_BYTES)
        {
          start_v += 2;
          L_VALUE = (int)(dataIn[INDEX_L + 1] << 8 + dataIn[INDEX_L + 2]);
        }
        else
        {
          L_VALUE = (int)dataIn[INDEX_L];
        }

        // V
        V_VALUE = new byte[L_VALUE];
        for (int cpt = 0; cpt < L_VALUE; cpt++)
        {
          if (dataIn.Length > cpt + start_v)
            V_VALUE[cpt] = dataIn[cpt + start_v];
        }
      }

    }


    private string decodeT(byte t)
    {
      switch (t)
      {
        case NULL_TLV: return "NULL";
        case LOCK_CONTROL_TLV: return "Lock Control";
        case MEMORY_CONTROL_TLV: return "Memory Control";
        case NDEF_MESSAGE_TLV: return "NDEF Message";
        case PROPRIETARY_TLV: return "Proprietary";
        case TERMINATOR_TLV: return "Terminator";
        default: return "Type unknown";
      }
    }

  
#endregion

#region NDEF decoding

    const byte MESSAGE_BEGIN_FLAG = 0x80;
    const byte MESSAGE_END_FLAG = 0x40;
    const byte CHUNK_FLAG = 0x20;
    const byte SHORT_RECORD_FLAG = 0x10;
    const byte ID_LENGTH_FLAG = 0x08;

    const byte SHORT_RECORD_HEADER = 0xD1;
    const byte SR_MB_HEADER = 0x91;
    const byte SR_ME_HEADER = 0x51;
    const byte RECORD_HEADER = 0xC1;

    // TNF constants
    const byte EMPTY = 0x00;
    const byte NFC_TYPE = 0x01;
    const byte MEDIA_TYPE = 0x02;
    const byte ABSOLUTE_URI = 0x03;
    const byte EXTERNAL_TYPE = 0x04;
    const byte UNKNOWN_TYPE = 0x05;
    const byte UNCHANGED = 0x06;
    const byte RESERVED = 0x07;


    string type_ndef_str = "";
    string record_name_str = "";
    string payload_str = "";
    int payload_size;

    byte[] payload_array;

    const int TYPE_LENGTH_INDEX = 1;
    const int PAYLOAD_LENGTH_INDEX = 2;

    string Card_URI_prefix = "";
    int TYPE_SP = 0x53;
    int TYPE_T = 0x54;
    int TYPE_U = 0x55;
    string Card_URI_content = "";
    string Card_URI_Tot = "";
    string Card_Title_lang = "";
    string Card_Title_content = "";
    bool Card_Title_present = false;
    int type_NDEF = 0;

    private string decodeTNF(byte tnf)
    {
      switch (tnf)
      {
        case EMPTY: return "Empty";
        case NFC_TYPE: return "NFC Forum well-known type";
        case MEDIA_TYPE: return "Media type as defined in RFC 2046";
        case ABSOLUTE_URI: return "Absolute URI as defined in RFC 3986";
        case EXTERNAL_TYPE: return "NFC Forum external type";
        case UNKNOWN_TYPE: return "Unknown";
        case UNCHANGED: return "Unchanged";
        case RESERVED: return "Reserved";
        default: return "Type unknown";
      }
    }

    NameValueCollection URI_Codes = new NameValueCollection();

    private void set_URI_Codes()
    {
      URI_Codes.Add("0x00", "");
      URI_Codes.Add("0x01", "http://www.");
      URI_Codes.Add("0x02", "https://www.");
      URI_Codes.Add("0x03", "http://");
      URI_Codes.Add("0x04", "https://");
      URI_Codes.Add("0x05", "tel:");
      URI_Codes.Add("0x06", "mailto:");
      URI_Codes.Add("0x07", "ftp://anonymous:anonymous@");
      URI_Codes.Add("0x08", "ftp://ftp.");
      URI_Codes.Add("0x09", "ftps://");
      URI_Codes.Add("0x0A", "sftp://");
      URI_Codes.Add("0x0B", "smb://");
      URI_Codes.Add("0x0C", "nfs://");
      URI_Codes.Add("0x0D", "ftp://");
      URI_Codes.Add("0x0E", "dav://");
      URI_Codes.Add("0x0F", "news:");
      URI_Codes.Add("0x10", "telnet://");
      URI_Codes.Add("0x11", "imap:");
      URI_Codes.Add("0x12", "rtsp://");
      URI_Codes.Add("0x13", "urn:");
      URI_Codes.Add("0x14", "pop:");
      URI_Codes.Add("0x15", "sip:");
      URI_Codes.Add("0x16", "sips:");
      URI_Codes.Add("0x17", "tftp:");
      URI_Codes.Add("0x18", "btspp://");
      URI_Codes.Add("0x19", "btl2cap://");
      URI_Codes.Add("0x1A", "btgoep://");
      URI_Codes.Add("0x1B", "tcpobex://");
      URI_Codes.Add("0x1C", "irdaobex://");
      URI_Codes.Add("0x1D", "file://");
      URI_Codes.Add("0x1E", "urn:epc:id:");
      URI_Codes.Add("0x1F", "urn:epc:tag:");
      URI_Codes.Add("0x20", "urn:epc:pat:");
      URI_Codes.Add("0x21", "urn:epc:raw:");
      URI_Codes.Add("0x22", "urn:epc:");
      URI_Codes.Add("0x23", "urn:nfc:");
      URI_Codes.Add("0xFE", "http://nfc.springcard.com/?");
     
    }

    private string URI_Identifier_Code(byte code)
    {
      return URI_Codes[byteToStringHex(code)];
    }

    private byte URI_Identifier_Code(string code)
    {
      byte value = 0x00;
      string value_str = "";
      foreach (string s in URI_Codes.AllKeys)
      {
        if (URI_Codes[s] == code)
          value_str = s;
      }
      if (value_str != "")
        value = StringToByte(value_str);
      return value;
    }

    private byte[] decodeNDEF(byte[] ndef)
    {
      int rest_index = 0;
      if (ndef.Length > 3)
      {
        int payload_length = 4;

        if ((ndef[0] & SHORT_RECORD_FLAG) != 0)
        {
          payload_length = 1;
          payload_size = (int)ndef[PAYLOAD_LENGTH_INDEX];
        }
        else
        {
          payload_size = (int)(
            ndef[PAYLOAD_LENGTH_INDEX] << 24
            + ndef[PAYLOAD_LENGTH_INDEX + 1] << 16
            + ndef[PAYLOAD_LENGTH_INDEX + 2] << 8
            + ndef[PAYLOAD_LENGTH_INDEX + 3]);
        }
        int type_length = (int)ndef[TYPE_LENGTH_INDEX];
        int type_index = PAYLOAD_LENGTH_INDEX + payload_length;
        int payload_index = type_index + type_length;
        if ((ndef[0] & ID_LENGTH_FLAG) != 0)
        {
          type_index++;
          payload_index += (int)ndef[PAYLOAD_LENGTH_INDEX + payload_length];
        }
        byte tnf = (byte)(ndef[0] & 0x07);
        type_ndef_str = decodeTNF(tnf);
        byte[] type_array = new byte[type_length];
        for (int cpt = 0; cpt < type_length; cpt++)
        {
          if (cpt + type_index < ndef.Length)
            type_array[cpt] = ndef[cpt + type_index];
        }
        record_name_str = createAsciiStrFromByteArray(type_array);
        rest_index = payload_index;
        if (payload_size > 0)
        {
          /* store payload in an array */
          payload_array = new byte[payload_size];
          for (int cpt = 0; cpt < payload_size; cpt++)
          {
            if (cpt + payload_index < ndef.Length)
              payload_array[cpt] = ndef[cpt + payload_index];
          }
          if (record_name_str == "Sp")
          {
            decodePayloadSp(payload_array);
            type_NDEF = TYPE_SP;
          }
          if (record_name_str == "U")
          {
            decodePayloadURI(payload_array);
            if (type_NDEF == 0)
            {
              type_NDEF = TYPE_U;
            }
          }
          if (record_name_str == "T")
          {
            decodePayloadText(payload_array);
            if (type_NDEF == 0)
            {
              type_NDEF = TYPE_T;
            }
          }

          /*payload_str = URI_Identifier_Code(ndef[payload_index]) +
            createAsciiStrFromByteArray(payload_array);*/
          rest_index = payload_index + payload_size;
        }
      }
      
      int rest_size = ndef.Length - rest_index;
      byte[] rest_array = new byte[0];
      if (rest_size > 0)
      {
        rest_array = new byte[rest_size];
        for (int cpt = 0; cpt < rest_size; cpt++)
        {
          if (cpt + rest_index < ndef.Length)
            rest_array[cpt] = ndef[cpt + rest_index];
        }
      }
      return rest_array;
    }




    #region decode Payload

    const int STATUS_INDEX = 0;
    const int LANGUAGE_CODE_INDEX = 1;

    private void decodePayloadText(byte[] payload)
    {
      Card_Title_present = true;
      /* first, decode status byte : retreive language code length */
      int langage_code_size = (int)(payload[STATUS_INDEX] & 0x3F);

      /* retreive language code */
      byte[] language_array = new byte[langage_code_size];
      for (int i = 0; i < langage_code_size; i++)
      {
        if (LANGUAGE_CODE_INDEX + i < payload.Length)
          language_array[i] = payload[LANGUAGE_CODE_INDEX + i];
      }
      Card_Title_lang = createAsciiStrFromByteArray(language_array);

      /* retreive text*/
      int text_index = LANGUAGE_CODE_INDEX + langage_code_size;
      if (text_index < payload.Length)
      {
        byte[] text_array = new byte[payload.Length - text_index];
        for (int i = 0; i < text_array.Length; i++)
        {
          if (i + text_index < payload.Length)
            text_array[i] = payload[i + text_index];
        }

        Card_Title_content = createAsciiStrFromByteArray(text_array);

      }


    }

    private void decodePayloadURI(byte[] payload)
    {
      if (payload.Length > 1)
      {
        byte[] payload_array_tmp = new byte[payload.Length - 1];
        for (int cpt = 0; cpt < payload_array_tmp.Length; cpt++)
        {
          payload_array_tmp[cpt] = payload[cpt + 1];
        }
        Card_URI_prefix = URI_Identifier_Code(payload[0]);
        Card_URI_content = createAsciiStrFromByteArray(payload_array_tmp);
        Card_URI_Tot = Card_URI_prefix + Card_URI_content;
        payload_str = URI_Identifier_Code(payload[0]) +
          createAsciiStrFromByteArray(payload_array_tmp);
      }

    }


    private void decodePayloadSp(byte[] payload)
    {
      byte[] rest;
      do
      {
        rest = decodeNDEF(payload);
        payload = rest;
      } while (rest.Length > 0);
    }

    #endregion

#endregion

#region conversion from str to byte and vice versa

    private byte[] createByteArrayFromStr(string StrIn)
    {
      string Str = StrIn.Replace("0x", "").Replace(" ", "");
      
      if (Str.IndexOf(" ") != -1)
      {
        string nStr = "";
        for (int cpt = 0; cpt < Str.Length; cpt = cpt + 2)
        {
          nStr = nStr + Str.Substring(cpt, 2) + " ";
        }
        Str = nStr;
      }

      // command stored into byte array
      char[] delimiterByte = new char[1];
      delimiterByte[0] = ' ';
      string properStr = Str;
      // removing last " "
      if (Str.EndsWith(" "))
      {
        properStr = Str.Substring(0, Str.Length - 1);
      }

      string[] Strbuff = properStr.Split(delimiterByte);
      byte[] StrBuffer = new byte[Strbuff.Length];
      for (int i = 0; i < Strbuff.Length; i++)
      {
        // converting byte string to byte  
        StrBuffer[i] = System.Convert.ToByte(int.Parse(Strbuff[i],
          System.Globalization.NumberStyles.HexNumber));
      }
      return StrBuffer;
    }

    private string createStrFromByteArray(byte[] byteArray)
    {
      string Str = "";
      if (byteArray.Length <= 0)
        return Str;
      for (int cpt = 0; cpt < byteArray.Length - 1; cpt++)
        Str += String.Format("{0:X02}", byteArray[cpt]) + " ";
      Str += String.Format("{0:X02}", byteArray[byteArray.Length - 1]);
      return Str;
    }

    private byte[] createByteArrayFromAsciiStr(string StrIn)
    {
      byte[] array = new byte[StrIn.Length];

      for (int cpt = 0; cpt < StrIn.Length; cpt++)
        array[cpt] = (byte)System.Convert.ToChar(StrIn[cpt]);

      return array;

    }

    private byte StringToByte(string StrIn)
    {
      string Str = StrIn.Replace("0x", "").Replace(" ", "");
      return System.Convert.ToByte(int.Parse(Str,
          System.Globalization.NumberStyles.HexNumber));
    }

    private string createAsciiStrFromByteArray(byte[] byteArray)
    {
      string Str = "";
      if (byteArray.Length <= 0)
        return Str;
      for (int cpt = 0; cpt < byteArray.Length; cpt++)
        Str += byteToAscii(byteArray[cpt]);
      return Str;
    }

    public string byteToAscii(byte value)
    {
      if (value < 32 || value > 127)
        return (".");
      else
        return (System.Convert.ToChar(value).ToString());
    }

    public string byteToString(byte value)
    {
      if (value < 32 || value > 127)
        return (String.Format("{0:X02}", value));
      else
        return (System.Convert.ToChar(value).ToString());
    }

    public string byteToStringHex(byte value)
    {
      return ("0x" + String.Format("{0:X02}", value));
    }
#endregion

#region form Closing
    private void MainFrm_Closed(object sender, EventArgs e)
    {
      if (readerActive)
        SPROX.ReaderClose();
    }

    private void MainFrm_Closing(object sender, CancelEventArgs e)
    {
      int retry = 50;

      workerAllowed = false;
      workerEvent.Set();

      while ((workerRunning) && (retry-- > 0))
        Thread.Sleep(20);

    }

    private void butEnd_Click(object sender, EventArgs e)
    {
      this.Close();
      this.Dispose();
    }


#endregion

#region panel prompt

    private int always_open = 0 ;
    private const int UNDEFINED = 0;
    private const int YES = 1;
    private const int NO = 2;
    private bool registry_read = false;
    private string URL_to_open = "";

    private void OpenPanel(string sUrl)
    {
      URL_to_open = sUrl;
      /* has the registry been read? */
      if (!registry_read)
      {
        ReadRegistery();
        registry_read = true;
      }
      if (always_open == YES)
      {
        /* always open URL */
        /* Next run in 2 seconds */
        timerBase.Interval = 2000;
        CodePPC.General.Tools.ShellExecute(sUrl);
        labelInfo.Text = "Opening URL " + sUrl + " , please wait ...";
        
      }
      else
      {
        if (always_open == UNDEFINED)
        {
          panelPrompt.Visible = true;
          labelQuestion.Text = "Would you like to open " + sUrl + " ?";
        }

      }
    }

    private void buttonNo_Click(object sender, EventArgs e)
    {
      panelPrompt.Visible = false;
      if (checkBoxAlways.Checked)
        WriteRegistery(false);
    }

    private void buttonYes_Click(object sender, EventArgs e)
    {
      panelPrompt.Visible = false;
      if (checkBoxAlways.Checked)
        WriteRegistery(true);
      /* open URL */
      /* Next run in 2 seconds */
      timerBase.Interval = 2000;
      CodePPC.General.Tools.ShellExecute(URL_to_open);
      labelInfo.Text = "Opening URL " + URL_to_open + " , please wait ...";
      
    }
#endregion

#region registy

    // Saving current config into registry 
    private void WriteRegistery(bool always_open_url)
    {
      try
      {
        CodePPC.General.Registry.CreateNewKey(CodePPC.General.Registry.HKEY_LOCAL_MACHINE,
          "\\Software\\SpringCard\\NFCDecoder");

        if (always_open_url)
        {
          CodePPC.General.Registry.WriteValue(CodePPC.General.Registry.HKEY_LOCAL_MACHINE,
            "\\Software\\SpringCard\\NFCDecoder", "always_open", "yes");
          always_open = YES;
        }
        else
        {
          CodePPC.General.Registry.WriteValue(CodePPC.General.Registry.HKEY_LOCAL_MACHINE,
            "\\Software\\SpringCard\\NFCDecoder","always_open","no");
          always_open = NO;
        }
      }
      catch (Exception e)
      {
        // cannot write registry
      }

    }

    public void ReadRegistery()
    {
      // settings according to config registered
      try
      {
        string registry_always_open = CodePPC.General.Registry.ReadValue(CodePPC.General.Registry.HKEY_LOCAL_MACHINE,
          "\\Software\\SpringCard\\NFCDecoder", "always_open");
        if (registry_always_open == "yes")
          always_open = YES;
        else
        {
          if (registry_always_open == "no")
            always_open = NO;
          else
            always_open = UNDEFINED;
        }
      }
      catch (Exception)
      {
        // do nothing, the key doesn't exist in register
      }
    }
#endregion

  }
}